#include <iostream>
#include <string>
#include <vector>
#include <memory>
#include <algorithm>
#include <sstream>
#include <fstream>
#include "regexp/Pattern.h"
#include "regexp/Matcher.h"
#include "nfw.h"

using namespace std;

nfw::nfw(int argc, char *argv[])
{
	for (int i = 0; i<argc; i++)
		this->paramters.push_back(argv[i]);
	this->populateGrammar();
	this->parseArguments();
}

void nfw::parseArguments()
{
	vector<string> rule_arr;
	vector<string> words;
	ifstream rulefile;
	string line;
	string rule;
	if (this->paramters.size() == 1)
	{
		cerr << "Wrong number of arguments" << endl;
	} else if ( this->paramters.size() == 2 && this->paramters.at(1) == "help" )
	{
		cout << "Some help information..." << endl;
		this->valid = true;
	} else if ( this->paramters.size() == 2 && (this->paramters.at(1) == "version" || this->paramters.at(1) == "ver" ) )
	{
		cout << "nfw version - " << NFW_VERSION << endl;
		cout << "regex library by Jeff Stuart (GPL) http://freshmeat.net/projects/cpp_regex/" << endl;
		this->valid = true;
	} else if ( this->paramters.size() == 3 && this->paramters.at(1) == "file" ) {
		//open file
		rulefile.open(this->paramters.at(2).c_str());
		if (rulefile.is_open())
		{
			while (! rulefile.eof() )
			{
				getline(rulefile, line);
				Pattern * p = Pattern::compile("([a-z0-9.\\-])+ (\\w|\"([^\"]*)\")+");
				rule_arr = p->findAll(line);
				delete p;
				this->split(line, ' ', words);
				if (words.at(0) == "custom")
				{
					//this->customRule(rule);
					for(unsigned int i = 1; i < words.size(); i++)
					{
						cout << words.at(i) << " ";
					}
					cout << endl;
				} else {
					if (this->validateRules(rule_arr))
					{
						this->valid = true;
						this->parseRule(rule_arr);
					} else {
						this->valid = false;
					}
				}
				words.clear();
				rule_arr.clear();
			}
			rulefile.close();
		}
	} else if ( this->paramters.size() > 3) {
		// parse rule from parameter
		for(unsigned int i = 1; i < this->paramters.size(); i++)
		{
			if (i == this->paramters.size())
				rule += this->paramters.at(i);
			else
				rule += this->paramters.at(i) + " ";
		}
		Pattern * p = Pattern::compile("([a-z0-9.\\-])+ (\\w|\"([^\"]*)\")+");
		rule_arr = p->findAll(rule);
		delete p;
		this->split(rule, ' ', words);
		if (words.at(0) == "custom")
		{
			//this->customRule(rule);
			for(unsigned int i = 1; i < words.size(); i++)
			{
				cout << words.at(i) << " ";
			}
			cout << endl;
		} else {
			if (this->validateRules(rule_arr))
			{
				this->valid = true;
				this->parseRule(rule_arr);
			} else {
				this->valid = false;
			}
		}
	} else {
		cerr << "Wrong number of arguments" << endl;
	}
}

void nfw::parseRule(vector<string>& rule_arr)
{
	// Parses a single rule
	string p1;
	string p2;
	string rule = "";
	string act = "";
	string chain = "";
	vector<string> parts;

	auto_ptr<Pattern> split(Pattern::compile("([a-z0-9.\\-]|\\w|\"([^\"]*)\")+"));
	auto_ptr<Pattern> action(Pattern::compile("(drop|deny|accept|log)"));
	auto_ptr<Pattern> ip(Pattern::compile("(?:\\d{1,3}\\.){3}\\d{1,3}"));
	auto_ptr<Pattern> iprange(Pattern::compile("(?:\\d{1,3}\\.){3}\\d{1,3}-(?:\\d{1,3}\\.){3}\\d{1,3}"));
	auto_ptr<Pattern> port(Pattern::compile("(\\d)*"));

	//auto_ptr<Matcher> actionmatch = action->createMatcher();
	//auto_ptr<Matcher> ipmatch = ip->createMatcher();
	//auto_ptr<Matcher> iprangematch = iprange->createMatcher();
	for(unsigned int i = 0; i < rule_arr.size(); i++)
	{
		parts = split->findAll(rule_arr.at(i));
		//this->split(rule_arr.at(i), ' ', parts);
		//rule_arr.spl
		p1 = parts.at(0);
		p2 = parts.at(1);
		parts.clear();
		Matcher * actionmatch = action->createMatcher(p1);
		Matcher * ipmatch = ip->createMatcher(p1);
		Matcher * iprangematch = iprange->createMatcher(p1);
		Matcher * portmatch = port->createMatcher(p1);
		if (actionmatch->matches())
		{
			act = p1;
			chain = p2;
		} else if ( iprangematch->matches() )
		{
			rule += "-m iprange ";
			if (p2 == "source")
				rule += "--src-range ";
			else
				rule += "--dst-range ";
			rule += p1 + " ";
		} else if ( ipmatch->matches() )
		{
			if (p2 == "source")
				rule += "-s ";
			else
				rule += "-d ";
			rule += p1 + " ";
		} else if ( portmatch->matches() )
		{
			if (p2 == "source")
				rule += "--sport ";
			else
				rule += "--dport ";
			rule += p1 + " ";
		} else {
			//parse the other rules that we won't bother matching with regex
			if (p1 == "comment")
			{
				//strip qoutes if exists
				if (p2.at(0) == '"')
				{
					p2.erase(0,1);
					p2.erase(p2.size() - 1, 1);
				}
				//p2.replace(p2.find("\""), 1, "");
				//p2.replace(p2.find("\""), 1, "");
				rule += "-m comment --comment \"" + p2 + "\" ";
			} else if (p1 == "protocol")
			{
				rule += "-p " + p2 + " ";
			}
		}
		delete actionmatch;
		delete ipmatch;
		delete iprangematch;
	}
	transform(chain.begin(), chain.end(), chain.begin(), ::toupper);
	transform(act.begin(), act.end(), act.begin(), ::toupper);
	cout << "-A " << chain << " " << rule << "-j " << act << endl;
}

void nfw::populateGrammar()
{
	// It doesn't seem to like (word|word) regex syntax, perhaps time to find a new regex library?
	//this->grammar.push_back("(\\w)* (input|output|forward){1}"); // <action> <chain>
	this->grammar.push_back("(\\w)* (\\w)*"); // <action> <chain>
	this->grammar.push_back("(?:\\d{1,3}\\.){3}\\d{1,3} (source|destination)"); // <ip> =: <ip_addr> <direction>
	this->grammar.push_back("(?:\\d{1,3}\\.){3}\\d{1,3}-(?:\\d{1,3}\\.){3}\\d{1,3} (source|destitation)"); // <iprange> =: <ip_addr>-<ip_addr> <direction>
	//this->grammar.push_back("(\\d)* (source|destitation)");//<port> =: # <direction>
	//this->grammar.push_back("recent (day|year|month)-(\\d)*-(\\d)*"); // <recent> =: recent <time>-#-#
	this->grammar.push_back("(\\d)* (\\w)*");//<port> =: # <direction>
	this->grammar.push_back("recent (\\w)*-(\\d)*-(\\d)*"); // <recent> =: recent <time>-#-#
	this->grammar.push_back("name (\\w){1}"); //<name> =: name <string>
	this->grammar.push_back("comment (\\w|\"([^\"]*)\")+"); //<comment> =: comment <string>
	this->grammar.push_back("protocol (tcp|udp)"); //<protocol> =: protocol ( tcp | udp )
	this->grammar.push_back("cstate (new|related|established|invalid)"); // <cstate> =: cstate { NEW | RELATED | ESTABLISHED | INVALID }
	this->grammar.push_back("state (new|related|established|invalid)"); // <state> =: state { NEW | RELATED | ESTABLISHED | INVALID }
}

bool nfw::isValid()
{
	return this->valid;
}

string nfw::getFalidRule()
{
	return this->failedrule;
}

bool nfw::validateRules(vector<std :: string> & rule_arr)
{
	for(unsigned int i = 0; i < rule_arr.size(); i++)
	{
		bool goodrule = false;
		for (unsigned int x = 0; x < this->grammar.size(); x++)
		{
			Pattern * grammar_pat = Pattern::compile(this->grammar.at(x));
			Matcher * grammar_match = grammar_pat->createMatcher(rule_arr.at(i));
			if (grammar_match->matches())
			{
				goodrule = true;
				break;
			}
			delete grammar_pat;
			delete grammar_match;
		}
		if (goodrule == false)
		{
			this->failedrule = rule_arr.at(i);
			return false;
		}
	}
	return true;
}

void nfw::split(const std::string &s, char delim, std::vector<std::string> &elems)
{
    std::stringstream ss(s);
    std::string item;
    while(std::getline(ss, item, delim)) {
        elems.push_back(item);
    }
}
